#include "VolumeHeader.h"
#include "json/json.h"

namespace OCPP
{
	namespace Volume {

		String VolumeHeader::generateHeader(GridData& gridData,
			size_t bodyLength,int numComponent,int pixelDatatype,
			const VolumeOptions& options)
		{
			const auto& extent = gridData.extent;

			float west = extent.rectangle.west;
			float south = extent.rectangle.south;
			float east = extent.rectangle.east;
			float north = extent.rectangle.north;

			//if (!options.knownNotLonLat)
			{
				west = Cesium::CesiumMath::toDegrees(west);
				south = Cesium::CesiumMath::toDegrees(south);
				east = Cesium::CesiumMath::toDegrees(east);
				north = Cesium::CesiumMath::toDegrees(north);
			}

			Json::Value  root;
			root["rectangle"]["west"] = west;
			root["rectangle"]["south"] = south;
			root["rectangle"]["east"] = east;
			root["rectangle"]["north"] = north;

			root["source"]["width"] = extent.dimX;
			root["source"]["height"] = extent.dimY;
			root["source"]["depth"] = extent.dimZ;

			float sizeDepth = 0.0f;

			if (extent.dimZ > 0)
			{
				auto& levelData = gridData.levelData;
				if (!levelData.empty())
				{
					sizeDepth = levelData[levelData.size() - 1];
					for (const auto& value : levelData)
					{
						root["size"]["levels"].append(value);
					}
				}
			}
			root["size"]["levelUinits"] = gridData.levelUinits;
			root["size"]["depth"] = sizeDepth;

			int pixelFormat = GL_RED;

			if (numComponent == 2)
			{
				pixelFormat = GL_RG;
			}
			else if (numComponent == 3)
			{
				pixelFormat = GL_RGB;
			}
			else if (numComponent == 4)
			{
				pixelFormat = GL_RGBA;
			}

			root["source"]["pixelFormat"] = pixelFormat;
			root["source"]["pixelDatatype"] = pixelDatatype;

			Json::FastWriter writer;
			String jsonStr = writer.write(root);
			int featureTableJSONByteLength = jsonStr.length();

			int padding = (4 - (featureTableJSONByteLength % 4)) % 4;
			featureTableJSONByteLength += padding;
			while (padding-- > 0) {
				jsonStr += ' ';
			}


			w3dmheader header;
			header.magic[0] = 'w', header.magic[1] = '3', header.magic[2] = 'd', header.magic[3] = 'm';
			header.version = 1;
			header.byteLength = sizeof(w3dmheader) + featureTableJSONByteLength + bodyLength;
			header.featureTableJSONByteLength = featureTableJSONByteLength;
			header.featureTableBinaryByteLength = bodyLength;

			String strHeader;
			strHeader.resize(sizeof(w3dmheader) + featureTableJSONByteLength);

			memcpy(&strHeader[0], (char*)&header, sizeof(w3dmheader));
			memcpy(&strHeader[sizeof(w3dmheader)], jsonStr.data(), jsonStr.size());

			return strHeader;
		}

		String VolumeHeader::getBytes(size_t bodyLength, int pixelDatatype)
		{
			Json::Value  root;
			root["rectangle"]["west"] = std::get<0>(extents);
			root["rectangle"]["south"] = std::get<1>(extents);
			root["rectangle"]["east"] = std::get<2>(extents);
			root["rectangle"]["north"] = std::get<3>(extents);

			int dimZ, dimY, dimX;
			root["source"]["width"] = dimX = std::get<0>(sizeOfSource);
			root["source"]["height"] = dimY = std::get<1>(sizeOfSource);
			root["source"]["depth"] = dimZ = std::get<2>(sizeOfSource);
			int cn = pixelDatatype == GL_FLOAT ? 4 : pixelDatatype == GL_HALF_FLOAT_CesiumUse ? 2 : 1;
			int comp = bodyLength / (dimZ * dimY * dimX) / cn;
			switch (comp)
			{
			case 4:
				pixelFormat = GL_RGBA;
				break;
			case 12:
			case 3:
				pixelFormat = GL_RGB;
				break;
			case 8:
			case 2:
				pixelFormat = GL_RG;
				break;
			default:
				if (pixelDatatype == GL_FLOAT || pixelDatatype == GL_HALF_FLOAT_CesiumUse)
				{
					pixelFormat = GL_RED;
				}
				else
				{
					pixelFormat = GL_ALPHA;
				}
				break;
			}


			root["source"]["pixelFormat"] = pixelFormat;
			root["source"]["pixelDatatype"] = pixelDatatype;

			root["size"]["width"] = std::get<0>(sizeInMeters);
			root["size"]["height"] = std::get<1>(sizeInMeters);
			root["size"]["depth"] = std::get<2>(sizeInMeters);

			Json::FastWriter writer;
			String jsonStr = writer.write(root);
			int featureTableJSONByteLength = jsonStr.length();

			int padding = 4 - (featureTableJSONByteLength % 4);
			featureTableJSONByteLength += padding;
			while (padding-- > 0) {
				jsonStr += ' ';
			}

			w3dmheader header;
			header.magic[0] = 'w', header.magic[1] = '3', header.magic[2] = 'd', header.magic[3] = 'm';
			header.version = 1;
			header.byteLength = sizeof(w3dmheader) + featureTableJSONByteLength + bodyLength;
			header.featureTableJSONByteLength = featureTableJSONByteLength;
			header.featureTableBinaryByteLength = bodyLength;

			String strHeader;
			strHeader.resize(sizeof(w3dmheader) + featureTableJSONByteLength);

			memcpy(&strHeader[0], (char*)&header, sizeof(w3dmheader));
			memcpy(&strHeader[sizeof(w3dmheader)], jsonStr.data(), jsonStr.size());

			return strHeader;
		}
	}
}